using Elastic.Clients.Elasticsearch;
using Elastic.Clients.Elasticsearch.QueryDsl;
using Elastic.Transport;
using CoreManager.Elasticsearch.Models;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace CoreManager.Elasticsearch.Services;

/// <summary>
/// Elasticsearch服务实现
/// </summary>
public class ElasticsearchService : IElasticsearchService
{
    private readonly ElasticsearchClient _client;
    private readonly ILogger<ElasticsearchService> _logger;
    private readonly ElasticsearchOptions _options;

    public ElasticsearchService(IOptions<ElasticsearchOptions> options, ILogger<ElasticsearchService> logger)
    {
        _options = options.Value;
        _logger = logger;

        var settings = new ElasticsearchClientSettings(new Uri(_options.Uri))
            .RequestTimeout(TimeSpan.FromSeconds(_options.TimeoutSeconds))
            .MaximumRetries(_options.MaxRetries);

        // 如果提供了用户名和密码，则使用基本认证
        if (!string.IsNullOrEmpty(_options.Username) && !string.IsNullOrEmpty(_options.Password))
        {
            settings.Authentication(new BasicAuthentication(_options.Username, _options.Password));
        }

        if (_options.EnableDebugMode)
        {
            settings.EnableDebugMode();
        }

        _client = new ElasticsearchClient(settings);
        _logger.LogInformation("Elasticsearch client initialized with URI: {Uri}", _options.Uri);
    }

    /// <inheritdoc />
    public async Task<bool> CreateIndexAsync(string indexName, CancellationToken cancellationToken = default)
    {
        try
        {
            var response = await _client.Indices.CreateAsync(indexName, cancellationToken);
            
            if (response.IsValidResponse)
            {
                _logger.LogInformation("Index {IndexName} created successfully", indexName);
                return true;
            }

            _logger.LogWarning("Failed to create index {IndexName}: {Error}", indexName, response.DebugInformation);
            return false;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error creating index {IndexName}", indexName);
            return false;
        }
    }

    /// <inheritdoc />
    public async Task<bool> DeleteIndexAsync(string indexName, CancellationToken cancellationToken = default)
    {
        try
        {
            var response = await _client.Indices.DeleteAsync(indexName, cancellationToken);
            
            if (response.IsValidResponse)
            {
                _logger.LogInformation("Index {IndexName} deleted successfully", indexName);
                return true;
            }

            _logger.LogWarning("Failed to delete index {IndexName}: {Error}", indexName, response.DebugInformation);
            return false;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error deleting index {IndexName}", indexName);
            return false;
        }
    }

    /// <inheritdoc />
    public async Task<bool> IndexExistsAsync(string indexName, CancellationToken cancellationToken = default)
    {
        try
        {
            var response = await _client.Indices.ExistsAsync(indexName, cancellationToken);
            return response.Exists;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error checking if index {IndexName} exists", indexName);
            return false;
        }
    }

    /// <inheritdoc />
    public async Task<IndexResult> IndexDocumentAsync<T>(string indexName, T document, string? documentId = null, CancellationToken cancellationToken = default) where T : class
    {
        try
        {
            var response = await _client.IndexAsync(document, idx => idx
                .Index(indexName)
                .Id(documentId), cancellationToken);

            if (response.IsValidResponse)
            {
                _logger.LogInformation("Document indexed successfully in {IndexName}, ID: {DocumentId}", indexName, response.Id);
                return new IndexResult
                {
                    IsSuccess = true,
                    DocumentId = response.Id,
                    IndexName = indexName
                };
            }

            _logger.LogWarning("Failed to index document in {IndexName}: {Error}", indexName, response.DebugInformation);
            return new IndexResult
            {
                IsSuccess = false,
                IndexName = indexName,
                ErrorMessage = response.DebugInformation
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error indexing document in {IndexName}", indexName);
            return new IndexResult
            {
                IsSuccess = false,
                IndexName = indexName,
                ErrorMessage = ex.Message
            };
        }
    }

    /// <inheritdoc />
    public async Task<List<IndexResult>> BulkIndexDocumentsAsync<T>(string indexName, IEnumerable<T> documents, CancellationToken cancellationToken = default) where T : class
    {
        var results = new List<IndexResult>();

        try
        {
            var response = await _client.BulkAsync(b => b
                .Index(indexName)
                .IndexMany(documents), cancellationToken);

            if (response.IsValidResponse)
            {
                foreach (var item in response.Items)
                {
                    results.Add(new IndexResult
                    {
                        IsSuccess = item.IsValid,
                        DocumentId = item.Id ?? string.Empty,
                        IndexName = indexName,
                        ErrorMessage = item.Error?.Reason
                    });
                }

                _logger.LogInformation("Bulk indexed {Count} documents in {IndexName}", documents.Count(), indexName);
            }
            else
            {
                _logger.LogWarning("Bulk index failed for {IndexName}: {Error}", indexName, response.DebugInformation);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error bulk indexing documents in {IndexName}", indexName);
        }

        return results;
    }

    /// <inheritdoc />
    public async Task<T?> GetDocumentAsync<T>(string indexName, string documentId, CancellationToken cancellationToken = default) where T : class
    {
        try
        {
            var response = await _client.GetAsync<T>(documentId, idx => idx.Index(indexName), cancellationToken);

            if (response.IsValidResponse && response.Found)
            {
                _logger.LogInformation("Document {DocumentId} retrieved from {IndexName}", documentId, indexName);
                return response.Source;
            }

            _logger.LogWarning("Document {DocumentId} not found in {IndexName}", documentId, indexName);
            return null;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting document {DocumentId} from {IndexName}", documentId, indexName);
            return null;
        }
    }

    /// <inheritdoc />
    public async Task<bool> DeleteDocumentAsync(string indexName, string documentId, CancellationToken cancellationToken = default)
    {
        try
        {
            var response = await _client.DeleteAsync(new Elastic.Clients.Elasticsearch.DeleteRequest(indexName, documentId), cancellationToken);

            if (response.IsValidResponse)
            {
                _logger.LogInformation("Document {DocumentId} deleted from {IndexName}", documentId, indexName);
                return true;
            }

            _logger.LogWarning("Failed to delete document {DocumentId} from {IndexName}: {Error}", documentId, indexName, response.DebugInformation);
            return false;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error deleting document {DocumentId} from {IndexName}", documentId, indexName);
            return false;
        }
    }

    /// <inheritdoc />
    public async Task<Models.SearchResponse<T>> SearchAsync<T>(string indexName, Models.SearchRequest request, CancellationToken cancellationToken = default) where T : class
    {
        var searchResponse = new Models.SearchResponse<T>
        {
            PageIndex = request.PageIndex,
            PageSize = request.PageSize
        };

        try
        {
            var from = (request.PageIndex - 1) * request.PageSize;
            
            Query query;
            var queries = new List<Query>();
            
            // 关键词搜索
            if (!string.IsNullOrEmpty(request.Query))
            {
                queries.Add(new MultiMatchQuery
                {
                    Query = request.Query,
                    Type = TextQueryType.BestFields
                });
            }

            // 时间范围过滤
            if (request.StartTime.HasValue || request.EndTime.HasValue)
            {
                queries.Add(new DateRangeQuery(new Elastic.Clients.Elasticsearch.Field("createdAt"))
                {
                    Gte = request.StartTime,
                    Lte = request.EndTime
                });
            }

            query = queries.Any()
                ? new BoolQuery { Must = queries }
                : new MatchAllQuery();
            
            var sortOrder = request.SortOrder.ToLower() == "asc" 
                ? SortOrder.Asc 
                : SortOrder.Desc;
            
            var response = await _client.SearchAsync<T>(s => s
                .Index(indexName)
                .From(from)
                .Size(request.PageSize)
                .Query(query)
                .Sort(so => so.Field(request.SortField, new FieldSort { Order = sortOrder })), 
                cancellationToken);

            if (response.IsValidResponse)
            {
                searchResponse.Total = response.Total;
                searchResponse.Items = response.Documents.ToList();
                searchResponse.TookMilliseconds = response.Took;

                _logger.LogInformation("Search in {IndexName} returned {Count} results in {Took}ms", 
                    indexName, searchResponse.Total, searchResponse.TookMilliseconds);
            }
            else
            {
                _logger.LogWarning("Search failed in {IndexName}: {Error}", indexName, response.DebugInformation);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error searching in {IndexName}", indexName);
        }

        return searchResponse;
    }

    /// <inheritdoc />
    public async Task<bool> PingAsync(CancellationToken cancellationToken = default)
    {
        try
        {
            var response = await _client.PingAsync(cancellationToken);
            return response.IsValidResponse;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error pinging Elasticsearch");
            return false;
        }
    }

    /// <inheritdoc />
    public async Task<object?> GetClusterHealthAsync(CancellationToken cancellationToken = default)
    {
        try
        {
            var response = await _client.Cluster.HealthAsync(cancellationToken);

            if (response.IsValidResponse)
            {
                return new
                {
                    ClusterName = response.ClusterName,
                    Status = response.Status.ToString(),
                    NumberOfNodes = response.NumberOfNodes,
                    NumberOfDataNodes = response.NumberOfDataNodes,
                    ActivePrimaryShards = response.ActivePrimaryShards,
                    ActiveShards = response.ActiveShards,
                    RelocatingShards = response.RelocatingShards,
                    InitializingShards = response.InitializingShards,
                    UnassignedShards = response.UnassignedShards
                };
            }

            _logger.LogWarning("Failed to get cluster health: {Error}", response.DebugInformation);
            return null;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting cluster health");
            return null;
        }
    }
}

